"""
Created on 2018/4/24 10:44 星期二
@author: Matt  zhuhan1401@126.com
Description: 使用决策树预测泰坦尼克号幸存者
"""

import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
from commonTool.plotCurve import plot_learning_curve, plot_param_curve
from sklearn.model_selection import train_test_split, cross_val_score, ShuffleSplit, GridSearchCV
from sklearn.tree import DecisionTreeClassifier


def loadDataset(fileName):
    data = pd.read_csv(fileName, index_col=0)  # 指定第一列为索引
    data.drop(['Ticket', 'Name', 'Cabin'], axis=1, inplace=True)  # inplace=True 直接对原data操作
    data['Sex'] = (data['Sex'] == 'male').astype('int')  # todo 处理性别数据
    # 处理登船港口数据
    labels = data['Embarked'].unique().tolist()
    data['Embarked'] = data['Embarked'].apply(lambda n: labels.index(n))
    # 处理丢失数据 填充0
    data = data.fillna(0)
    # print(data.head(20))
    return data


# 选择决策树的max_depth参数
def CVScore(XTrain, YTrain, XTest, YTest, d):
    clf = DecisionTreeClassifier(max_depth=d)
    clf.fit(XTrain, YTrain)
    trainScore = clf.score(XTrain, YTrain)
    testScore = clf.score(XTest, YTest)
    return (trainScore, testScore)


train = loadDataset('titanic/train.csv')
X = train.drop(['Survived'], axis=1).values
Y = train['Survived'].values

XTrain, XTest, YTrain, YTest = train_test_split(X, Y, test_size=0.2)
# print('train dataset:{0},test dataset:{1}'.format(XTrain.shape, XTest.shape))

clf = DecisionTreeClassifier()
clf.fit(XTrain, YTrain)
trainScoreWithoutChoose = clf.score(XTrain, YTrain)
testScoreWithoutChoose = clf.score(XTest, YTest)
# print('trainScoreWithoutChoose:{0},testScoreWithoutChoose:{1}'.format(trainScoreWithoutChoose, testScoreWithoutChoose))

cv = ShuffleSplit(test_size=0.2)
# plt.figure(figsize=(10, 6), dpi=200)
# plot_learning_curve(clf, "Learn Curve for DecisionTree", X, Y, ylim=(0.0, 1.01), cv=cv)
# plt.show()  # 描绘学习曲线 据图知 训练样本评分较低 测试样本与训练样本距离较大 为欠拟合！

"""

# 选择模型参数 max_depth
depths=range(2,15)
scores=[CVScore(XTrain, YTrain, XTest, YTest,d) for d in depths]
trainScores=[s[0] for s in scores]
CVScores=[s[1] for s in scores]
bestScoreIndex=np.argmax(CVScores)
bestScore=np.max(CVScores)
bestParam=depths[bestScoreIndex]
print('The bestParam is :{0} and the best Score is:{1} ,which its index is :{2}'.format(bestParam,bestScore,bestScoreIndex))

plt.figure(figsize=(6, 4), dpi=144)
plt.grid()
plt.xlabel('max depth of decision tree')
plt.ylabel('score')
plt.plot(depths,CVScores,'.g-',label='cross-validation score')
plt.plot(depths,trainScores,'.r--',label='training score')
plt.legend()
plt.show()
"""

# 模型优化有两个问题：
#  1. 数据不稳定,每次重新运行代码后所选择出来的模型参数不一样,这是因为每次划分训练样本和交叉训练样本时都是随机划分的,导致训练集有差异,训练出来的模型也有所差异
#  2. 一次只能选择一个参数,例如上述的最大决策树深度
# 所幸的是,我们可以使用model_selection 中的模型选择和评估工具,例如 GridSearchCV ,他能够枚举所提供列表里所有值来构建模型,多次计算训练模型并为模型评分

entropyThresholds=np.linspace(0,1,50)
giniThresholds=np.linspace(0,0.5,50)
thresholds=[('entropyThresholds',entropyThresholds),('giniThresholds',giniThresholds)]

paramGrid = [{'criterion':['entropy'],
              'min_impurity_decrease': entropyThresholds},
             {'criterion':['gini'],
              'min_impurity_decrease': entropyThresholds},
             {'max_depth':range(2,10)},
             {'min_samples_split': range(2,20,2)} ]  # 设置参数矩阵
clf2 = GridSearchCV(DecisionTreeClassifier(), paramGrid, cv=5,return_train_score=True)
clf2.fit(X, Y)
print("clf2.shape:{0}".format(clf2.cv_results_['mean_test_score']))
print("best param:{0}\n best score:{1}".format(clf2.best_params_, clf2.best_score_))
# for t in thresholds:
    # print("t:{0};t[0]:{1};t[1]:{2}".format(t,t[0],t[1]))
plot_param_curve(np.linspace(0,1,117), clf2.cv_results_, xlabel='entropyThresholds')
plt.show()

print()